/*******************************************************************************
 * Copyright (c) 2008 Sylvain Sicard.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Fabienne Boyer - initial API and implementation
 ******************************************************************************/
package fr.jade.reflex.factory;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.Type;
import org.objectweb.fractal.api.factory.InstantiationException;

import fr.jade.fraclite.InitializationContext;
import fr.jade.reflex.api.control.ReflexController;
import fr.jade.reflex.api.factory.GenericFactoryNotification;
import fr.jade.reflex.util.Reflex;
import fr.jade.util.log.Logger;
import fr.jade.util.log.config.LogReflex;

/**
 * Provides an implementation of the
 * {@link org.objectweb.fractal.api.factory.GenericFactory} interface which
 * manages the causal connection with the dual (i.e meta/execution) level : any
 * newFcInstance operation performed on the current factory is forwarded to the
 * factory of the dual level.
 * 
 * @author Fabienne Boyer 25/05/05
 * @contact Fabienne.Boyer@inrialpes.fr
 * 
 * More informations about this class: - define this class as a mixin which can
 * be added to any GenericFactory in order to make it reflexive - A "reflexive"
 * factory is a standard factory except that it has a specific additionnal
 * controller (ReflexController) which allows to get the reference of its dual
 * factory - A "reflexive" factory ensures that for any component it creates, it
 * notifies its dual factory such that a dual component is automatically
 * created. - the level of the current factory is set according to the
 * reflex-fractal.level system property - at instanciation time: - the factory
 * automatically register itself under the naming service (by using as name the
 * concatenation of the name given by the reflex-fractal.name system property
 * and the level), - the factory automatically connects itself with its dual
 * factory
 * 
 */
public class ReflexCompositeGenericFactory extends CompositeGenericFactory implements GenericFactoryNotification {

  protected String level = null;
  protected ReflexController weaveableRC = null;


  // -------------------------------------------------------------------------
  // Fields and methods added and overriden by the mixin class
  // -------------------------------------------------------------------------

  public void initFcController(final InitializationContext ic) throws InstantiationException {
    super.initFcController(ic);
    weaveableRC = (ReflexController) ic.getInterface("reflex-controller");
  }

  protected GenericFactoryNotification getDualCtrl() {
    assert weaveableRC != null : "Component must have a ReflexController";
    Component dual = weaveableRC.getCmpRef();
    assert dual != null : "Component must have a dual";
    GenericFactoryNotification dualCtrl = null;
    try {
      dualCtrl = Reflex.getGenericFactoryNotification(dual);
    } catch (NoSuchInterfaceException ignored) {    }
    assert dualCtrl != null : "Dual component must have a GenericAttributeNotificationController";
    return dualCtrl;
  }
  
  /**
   * Calls the overriden newFcInstance method and then notifies the dual factory
   * of the newFcInstance operation.
   * 
   * @param type
   *          an arbitrary component type.
   * @param controllerDesc
   *          a description of the controller part of the component to be
   *          created. This description is implementation specific. If it is
   *          <tt>null</tt> then a "default" controller part will be used.
   * @param contentDesc
   *          a description of the content part of the component to be created.
   *          This description is implementation specific. It may be
   *          <tt>null</tt> to create component with an empty initial content.
   *          It may also be, in Java, the fully qualified name of a Java class,
   *          to create primitive components.
   * @return the {@link Component} interface of the created component.
   * @throws InstantiationException
   *           if the component cannot be created.
   */

  public Component newFcInstance(final Type type, final Object controllerDesc, final Object contentDesc) throws InstantiationException {

    // get the notification controller interface of the dual factory
    GenericFactoryNotification dualCtrl = getDualCtrl();
    
    // create a component associated to reflexive controllers
    Component cmp = newFcInstanceNotification(type, controllerDesc, contentDesc);
    // notify the dual factory of the newFcInstance operation
    Component dualCmp = dualCtrl.newFcInstanceNotification(type, controllerDesc, contentDesc);
    
    // set the component's dual reference
    Reflex.getReflexController(cmp).setCmpRef(dualCmp);
    Reflex.getReflexController(dualCmp).setCmpRef(cmp);

    return cmp;
  }

  /**
   * Calls the overriden newFcInstance method.
   * 
   * @param type
   *          an arbitrary component type.
   * @param controllerDesc
   *          a description of the controller part of the component to be
   *          created. This description is implementation specific. If it is
   *          <tt>null</tt> then a "default" controller part will be used.
   * @param contentDesc
   *          a description of the content part of the component to be created.
   *          This description is implementation specific. It may be
   *          <tt>null</tt> to create component with an empty initial content.
   *          It may also be, in Java, the fully qualified name of a Java class,
   *          to create primitive components.
   * @return the {@link Component} interface of the created component.
   * @throws InstantiationException
   *           if the component cannot be created.
   */

  public Component newFcInstanceNotification(Type type, Object controllerDesc, Object contentDesc) throws InstantiationException {
    Object cd = weaveableRC.getLevel() == ReflexController.META_LEVEL ? null : contentDesc;
    Logger.println(LogReflex.debug, "newFcInstanceNotification " + controllerDesc + " / " + cd + "(" + weaveableRC.getLevel() + ")");
    Component cmp = super.newFcInstance(type, controllerDesc, cd);
    // Set the contentDesc and controllerDesc attributes
    ReflexController rc = Reflex.getReflexController(cmp);
    rc.setReflexAttribute("controllerDesc", (String) (controllerDesc));
    rc.setReflexAttribute("contentDesc", (String) (contentDesc));
    // set the component's level
    rc.setLevel(weaveableRC.getLevel());

    return cmp;
  }

}
